package lc;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.*;

import org.junit.*;

public class Ex1114 {
    class Foo1 {

        private Lock lock;
        private Condition a, b, c;
    
        public Foo1() {
            lock = new ReentrantLock();
            a = lock.newCondition();
            b = lock.newCondition();
            c = lock.newCondition();
        }
    
        public void first(Runnable printFirst) throws InterruptedException {
            try {
                lock.lock();
                // printFirst.run() outputs "first". Do not change or remove this line.
                printFirst.run();
                b.signal();
            } finally {
                lock.unlock();
            }
        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            try {
                b.await();
                // printSecond.run() outputs "second". Do not change or remove this line.
                printSecond.run();
                c.signal(); 
            } finally {
                lock.unlock();
            }
        }
    
        public void third(Runnable printThird) throws InterruptedException {
            
            try {
                c.await();
                // printThird.run() outputs "third". Do not change or remove this line.
                printThird.run();
                a.signal();
            } catch (Exception e) {
                lock.unlock();
            }
        }
    }

    class Foo2 {

        private AtomicInteger i;
    
        public Foo2() {
            i = new AtomicInteger(1);
        }
    
        public void first(Runnable printFirst) throws InterruptedException {
            
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            i.incrementAndGet();
        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            
            if (i.get() < 2) {

            }
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            i.incrementAndGet();
        }
    
        public void third(Runnable printThird) throws InterruptedException {
            
            if (i.get() < 3) {

            }
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
            i.set(1);
        }
    }

    class Foo3 {

        private Semaphore semaphore1, semaphore2;
    
        public Foo3() {
            semaphore1 = new Semaphore(1);
            semaphore2 = new Semaphore(1);
        }
    
        public void first(Runnable printFirst) throws InterruptedException {

                // printFirst.run() outputs "first". Do not change or remove this line.
                printFirst.run();
                semaphore1.release();

        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            
            semaphore1.acquire();
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            semaphore2.release();

        }
    
        public void third(Runnable printThird) throws InterruptedException {
            semaphore2.acquire();
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        }
    }

    class Foo {

        private CountDownLatch count1, count2;
    
        public Foo() {
            count1 = new CountDownLatch(1);
            count2 = new CountDownLatch(2);
        }
    
        public void first(Runnable printFirst) throws InterruptedException {

                // printFirst.run() outputs "first". Do not change or remove this line.
                printFirst.run();
                count1.countDown();

        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            
            count1.await();
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            count2.countDown();

        }
    
        public void third(Runnable printThird) throws InterruptedException {
            count2.await();
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        }
    }
}
